package cbr

import (
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"errors"
	"fmt"
	"math/big"
	"strings"
	"sync"
	"ebbflow/pkg/crypto"
)

// Minimal proof-chain interface placeholders to align with paper terminology.
// These are lightweight and optional; callers can inject real implementations later.

// Evidence represents a generic evidence blob (opaque to the framework).
type Evidence struct {
	Type string
	Data []byte
}

// ProofVerifier defines a pluggable verifier.
// Return nil error to accept, or a non-nil error to reject.
type ProofVerifier interface {
	Verify(e Evidence) error
}

// NoopVerifier is a default verifier that always accepts.
type NoopVerifier struct{}

func (NoopVerifier) Verify(e Evidence) error { return nil }

// ProofEntry is a lightweight per-epoch proof record (for audit/logging only).
// It does not affect protocol correctness; cryptographic guarantees remain with
// MVBA/ACSS/BLS mechanisms. This mirrors the intent of a "proof chain" summary.
type ProofEntry struct {
	Epoch         int      `json:"epoch"`
	N             int      `json:"n"`
	F             int      `json:"f"`
	L             int      `json:"l"`
	Members       []int    `json:"members,omitempty"`
	MembersHash   string   `json:"members_hash,omitempty"`
	AggSigSummary string   `json:"agg_sig_summary,omitempty"`
	Sig           []byte   `json:"sig,omitempty"`
}

// BuildEpochDigest hashes epoch metadata to a 32-byte digest for signing.
func BuildEpochDigest(epoch int, n int, f int, l int, members []int) [32]byte {
	h := sha256.New()
	fmt.Fprintf(h, "e=%d;n=%d;f=%d;l=%d;members=", epoch, n, f, l)
	for i, m := range members {
		if i > 0 {
			h.Write([]byte{','})
		}
		fmt.Fprintf(h, "%d", m)
	}
	var out [32]byte
	copy(out[:], h.Sum(nil))
	return out
}

// SignEpochDigest signs the digest with current curve (secp256k1 by default).
// For demo we use a deterministic private key derived from members hash, to avoid
// external key distribution in this experimental code.
func SignEpochDigest(digest [32]byte, membersHash string) ([]byte, error) {
	// derive a pseudo key from membersHash
	b, err := hex.DecodeString(strings.TrimPrefix(membersHash, "0x"))
	if err != nil || len(b) == 0 {
		sum := sha256.Sum256([]byte(membersHash))
		b = sum[:]
	}
	sk := crypto.NewSecp256k1Scalar(new(big.Int).SetBytes(b))
	if sk == nil {
		return nil, errors.New("failed to derive sk")
	}
	sig := sk.Sign(digest[:])
	out, err := json.Marshal(sig)
	if err != nil {
		return nil, err
	}
	return out, nil
}

// VerifyEpochSignature verifies signature against derived public key
func VerifyEpochSignature(digest [32]byte, membersHash string, sig []byte) bool {
	b, err := hex.DecodeString(strings.TrimPrefix(membersHash, "0x"))
	if err != nil || len(b) == 0 {
		sum := sha256.Sum256([]byte(membersHash))
		b = sum[:]
	}
	sk := crypto.NewSecp256k1Scalar(new(big.Int).SetBytes(b))
	if sk == nil {
		return false
	}
	var s crypto.Secp256k1Signature
	if err := json.Unmarshal(sig, &s); err != nil {
		return false
	}
	pk := crypto.GetSecp256k1Generator().ScalarMult(sk)
	return pk.Verify(digest[:], &s)
}

// BuildAndSignProof fills MembersHash, AggSigSummary and Sig.
func BuildAndSignProof(pe *ProofEntry) error {
	digest := BuildEpochDigest(pe.Epoch, pe.N, pe.F, pe.L, pe.Members)
	// members hash = sha256(members list)
	mh := sha256.Sum256([]byte(fmt.Sprintf("%v", pe.Members)))
	pe.MembersHash = "0x" + hex.EncodeToString(mh[:])
	sig, err := SignEpochDigest(digest, pe.MembersHash)
	if err != nil {
		return err
	}
	pe.Sig = sig
	pe.AggSigSummary = fmt.Sprintf("secp256k1|len=%d", len(sig))
	return nil
}

// SigVerifier implements ProofVerifier on ProofEntry
type SigVerifier struct{}

func (SigVerifier) Verify(e Evidence) error {
	// expect Evidence.Data to be hex(digest)||hex(sig)||membersHash
	parts := strings.Split(string(e.Data), "|")
	if len(parts) != 3 {
		return errors.New("invalid evidence format")
	}
	digB, err := hex.DecodeString(parts[0])
	if err != nil || len(digB) != 32 {
		return errors.New("bad digest")
	}
	var dig [32]byte
	copy(dig[:], digB)
	sigB, err := hex.DecodeString(parts[1])
	if err != nil {
		return errors.New("bad sig")
	}
	mh := parts[2]
	if !VerifyEpochSignature(dig, mh, sigB) {
		return errors.New("sig verify failed")
	}
	return nil
}

// ProofChain maintains a chain of proof entries for audit/logging
type ProofChain struct {
	entries []*ProofEntry
	mu      sync.RWMutex
}

// NewProofChain creates a new proof chain
func NewProofChain() *ProofChain {
	return &ProofChain{
		entries: make([]*ProofEntry, 0),
	}
}

// AddEntry adds a new proof entry to the chain
func (pc *ProofChain) AddEntry(entry *ProofEntry) error {
	pc.mu.Lock()
	defer pc.mu.Unlock()
	
	if entry == nil {
		return errors.New("entry cannot be nil")
	}
	
	// Build and sign the proof
	if err := BuildAndSignProof(entry); err != nil {
		return fmt.Errorf("failed to build and sign proof: %w", err)
	}
	
	pc.entries = append(pc.entries, entry)
	return nil
}

// GetEntry retrieves a proof entry by epoch
func (pc *ProofChain) GetEntry(epoch int) *ProofEntry {
	pc.mu.RLock()
	defer pc.mu.RUnlock()
	
	for _, entry := range pc.entries {
		if entry.Epoch == epoch {
			return entry
		}
	}
	return nil
}

// GetAllEntries returns all proof entries
func (pc *ProofChain) GetAllEntries() []*ProofEntry {
	pc.mu.RLock()
	defer pc.mu.RUnlock()
	
	result := make([]*ProofEntry, len(pc.entries))
	copy(result, pc.entries)
	return result
}

// Length returns the number of entries in the chain
func (pc *ProofChain) Length() int {
	pc.mu.RLock()
	defer pc.mu.RUnlock()
	return len(pc.entries)
}

